/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.baatar.opt.pga.impl.service;

import java.util.ArrayList;
import java.util.concurrent.LinkedBlockingQueue;
import org.apache.log4j.Logger;
import org.baatar.opt.genetic.AlgorithmParameters;
import org.baatar.opt.genetic.chromosome.MigrantChromosome;
import org.baatar.opt.pga.intf.IPGACoordinator;
import org.baatar.remoting.service.IImplementor;
import org.baatar.remoting.service.RemotingService;
import org.baatar.remoting.service.RemotingServiceInfo;
import org.baatar.thread.ThreadManager;
import org.baatar.thread.ThreadStatus;

/**
 *
 * @author Ba
 */
public class PGACoordinatorServiceImpl<T, M> extends ThreadManager implements IImplementor, IPGACoordinator<T, M> {

    private RemotingService remotingService = null;
    private Logger logger = null;

    private ArrayList<Cluster<T, M>> clusters = null;
    private AlgorithmParameters<T, M> algoParams = null;

    @Override
    public synchronized Integer registerCluster(RemotingServiceInfo clusterNetInfo, Integer numOfWorkers) throws Exception {
        boolean add = true;
        for (int i = 0; i < clusters.size(); i++)
            if (clusters.get(i).getClusterNetInfo().equals(clusterNetInfo)) {
                add = false;
                break;
            }
        if (add) {
            if (clusters.add(new Cluster<T, M>(remotingService.getRemotingServiceInfo(),
                                                clusterNetInfo,
                                                clusters.size(),
                                                numOfWorkers)))
                return clusters.size() - 1;
        }
        return null;
    }

    @Override
    public AlgorithmParameters<T, M> getAlgorithmParameters() throws Exception {
        return algoParams;
    }

    private LinkedBlockingQueue<MigrantChromosome<M>[]> migrantQueue = new LinkedBlockingQueue<MigrantChromosome<M>[]>();

    @Override
    public void migrate(Integer clusterIndex, Integer optimizerIndex, Integer iteration, MigrantChromosome<M> migrants[]) throws Exception {
//        logger.info("(" + clusterIndex + "," + optimizerIndex + "): " + iteration + " - " + migrant.getFitness());
//        clusters.get(clusterIndex).setLastMigrant(optimizerIndex, migrant);

        migrantQueue.add(migrants);
    }

    public int prepareOptimizers() throws Exception {
        int numOfOptPrepared = 0;
        for (int i = 0; i < clusters.size(); i++) {
            numOfOptPrepared += clusters.get(i).getCluster().prepareOptimizers();
        }
        return numOfOptPrepared;
    }

    public int runOptimizers() throws Exception {
        int numOfOptStarted = 0;
        for (int i = 0; i < clusters.size(); i++) {
            numOfOptStarted += clusters.get(i).getCluster().runOptimizers();
        }
        return numOfOptStarted;
    }

    @Override
    public void reportProgress(Integer clusterIndex, Integer optimizerIndex, Integer iteration, String progress) throws Exception {
        Cluster<T, M> cluster = clusters.get(clusterIndex);
        if (cluster != null) {
//            logger.info("(" + clusterIndex + "," + optimizerIndex + "): " + iteration + " - " + progress);
            cluster.addConvergenceInfo(optimizerIndex, iteration, progress);
            if (iteration == algoParams.getMaxNumOfIterations()) {
                logger.info(cluster.getConvergenceInfo(optimizerIndex));
            }
        }
    }

    @Override
    protected boolean initializeManager() throws Exception {
        clusters = new ArrayList<Cluster<T, M>>();
        return true;
    }

    @Override
    protected void finalizeManager() throws Exception {
        clusters.clear();
        clusters = null;
    }

    @Override
    protected void goAhead() throws Exception {
        while (!processCancelled()) {
            MigrantChromosome<M>[] migrants = migrantQueue.take();
            for (int i = 0; i < clusters.size(); i++) {
                Cluster<T, M> cluster = clusters.get(i);
                cluster.getCluster().migrate(migrants);
            }
        }
    }

    @Override
    protected void statusIsBeingChanged(ThreadStatus oldThreadStatus, ThreadStatus newThreadStatus) {
    }

    @Override
    protected void statusChanged(ThreadStatus oldThreadStatus, ThreadStatus newThreadStatus) {
    }

    /*
     * {@inheritDoc}
     */
    @Override
    public void setRemotingService(RemotingService remotingService) {
        this.remotingService = remotingService;
    }

    /*
     * {@inheritDoc}
     */
    @Override
    public void setLogger(Logger logger) {
        this.logger = logger;
    }

    /**
     * @param algoParams the algoPref to set
     */
    public void setAlgoParams(AlgorithmParameters<T, M> algoParams) {
        this.algoParams = algoParams;
    }

    /**
     * @return the algoPref
     */
    public AlgorithmParameters<T, M> getAlgoParams() {
        return algoParams;
    }

    public void addCluster(Cluster<T, M> cluster) {
        clusters.add(cluster);
    }
}
